JavaScript Proxy handler'larını sağlam doğrulama ve tür güvenliği için keşfedin. Daha temiz, daha güvenilir kod için nesne işlemlerini nasıl yakalayacağınızı ve kısıtlamaları nasıl uygulayacağınızı öğrenin.
JavaScript Proxy Handler Doğrulama: Tür Güvenli Nesne Yakalama
JavaScript Proxy'leri, temel nesne işlemlerini yakalamak ve özelleştirmek için güçlü bir mekanizma sağlar. En önemli kullanım alanlarından biri veri doğrulamadır. Proxy handler'larından yararlanarak, nesne özelliklerine kısıtlamalar ve tür güvenliği uygulayabilir, bu da daha sağlam ve sürdürülebilir bir koda yol açar. Bu blog yazısı, JavaScript Proxy'lerini etkili nesne doğrulaması için nasıl kullanacağınızı, pratik örnekler ve her seviyedeki geliştiriciler için rehberlik sunarak araştırıyor. Çeşitli handler metotlarını ele alacak ve veri bütünlüğünü sağlamak için bunların nasıl kullanılabileceğini göstereceğiz.
JavaScript Proxy'lerini Anlamak
Doğrulamaya dalmadan önce, JavaScript Proxy'lerinin ne olduğunu ve nasıl çalıştığını kısaca gözden geçirelim. Bir Proxy nesnesi, başka bir nesneyi (hedef) sarar ve bu hedef üzerinde gerçekleştirilen işlemleri yakalar. Proxy, bir özellik almak, bir özellik ayarlamak, bir fonksiyonu çağırmak veya yeni bir nesne oluşturmak gibi işlemler için özel davranışlar tanımlamanıza olanak tanır. Bu özelleştirme, belirli işlemleri yakalayan metotlar içeren bir handler aracılığıyla gerçekleştirilir.
Bir Proxy oluşturmak için temel sözdizimi şöyledir:
const proxy = new Proxy(target, handler);
- target: Proxy ile sarılacak nesne.
- handler: Hedef üzerindeki işlemleri yakalayan metotlar (tuzaklar) içeren bir nesne.
Doğrulama için Proxy Handler Metotları
Handler nesnesi, her biri hedef nesne üzerinde farklı bir işleme karşılık gelen çeşitli metotlar içerebilir. İşte doğrulama için en alakalı metotlardan bazıları:
- get(target, property, receiver): Özellik erişimini yakalar.
- set(target, property, value, receiver): Özellik atamasını yakalar.
- apply(target, thisArg, argumentsList): Fonksiyon çağrılarını yakalar.
- construct(target, argumentsList, newTarget):
newoperatörünü yakalar. - deleteProperty(target, property):
deleteoperatörünü yakalar. - defineProperty(target, property, descriptor): Özellik tanımını yakalar.
- has(target, property):
inoperatörünü yakalar. - ownKeys(target):
Object.getOwnPropertyNames(),Object.getOwnPropertySymbols()veReflect.ownKeys()öğelerini yakalar. - preventExtensions(target):
Object.preventExtensions()öğesini yakalar. - getPrototypeOf(target):
Object.getPrototypeOf()öğesini yakalar. - setPrototypeOf(target, prototype):
Object.setPrototypeOf()öğesini yakalar.
Doğrulama amacıyla en sık kullanılanlar oldukları için öncelikle get, set, apply ve construct handler'larına odaklanacağız.
set Handler'ı ile Özellik Atamalarını Doğrulama
set handler'ı, özellik atamalarını doğrulamak için çok önemlidir. Bir nesnenin özelliklerini değiştirmeye yönelik girişimleri yakalamanıza ve atama gerçekleşmeden önce kısıtlamalar uygulamanıza olanak tanır.
Örnek: Tür Kontrolü
Bir Person nesnesinin özellikleri için tür kontrolünü zorlayan bir Proxy oluşturalım. name'in her zaman bir string ve age'in her zaman bir sayı olduğundan emin olacağız.
const person = {
name: 'John Doe',
age: 30
};
const validator = {
set: function(target, property, value) {
if (property === 'name' && typeof value !== 'string') {
throw new TypeError('Name must be a string');
}
if (property === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
// The following line is crucial for ensuring the property is actually set.
target[property] = value;
return true; // Indicate success
}
};
const proxy = new Proxy(person, validator);
proxy.name = 'Jane Smith'; // Works fine
proxy.age = 25; // Works fine
try {
proxy.age = '40'; // Throws TypeError
} catch (e) {
console.error(e);
}
console.log(proxy.age); // Output: 25
Bu örnekte, set handler'ı, name ve age'e atanan değerin türünü kontrol eder. Tür yanlışsa, bir TypeError fırlatarak atamayı engeller. Değeri gerçekten ayarlamak için `target[property] = value;` öğesini handler içine eklemek önemlidir; aksi takdirde, özellik güncellenmez.
Örnek: Aralık Doğrulaması
Ayrıca bir özelliğin belirli bir aralığa girdiğini de doğrulayabiliriz. Örneğin, age'in her zaman 0 ile 120 arasında olduğundan emin olalım.
const person = {
name: 'John Doe',
age: 30
};
const validator = {
set: function(target, property, value) {
if (property === 'age') {
if (typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
if (value < 0 || value > 120) {
throw new RangeError('Age must be between 0 and 120');
}
}
target[property] = value;
return true;
}
};
const proxy = new Proxy(person, validator);
proxy.age = 50; // Works fine
try {
proxy.age = -5; // Throws RangeError
} catch (e) {
console.error(e);
}
get Handler'ı ile Özellik Erişimi Doğrulama
Sıkı doğrulama için daha az yaygın olsa da, get handler'ı, bir özelliğe erişildiğinde dönüştürmeler veya doğrulamalar gerçekleştirmek için kullanılabilir. Örneğin, bir telefon numarasını biçimlendirmek veya iade etmeden önce bir tarihin geçerli olduğundan emin olmak isteyebilirsiniz.
Örnek: Salt Okunur Özellikler
Doğrudan okunmaması gereken bir özelliğe erişmeye çalışıldığında bir hata atarak salt okunur özellikleri simüle edebilirsiniz.
const config = {
apiKey: 'secret_key'
};
const validator = {
get: function(target, property) {
if (property === 'apiKey') {
throw new Error('Cannot directly access apiKey. Use a secure method.');
}
return target[property];
}
};
const proxy = new Proxy(config, validator);
try {
console.log(proxy.apiKey); // Throws Error
} catch (e) {
console.error(e);
}
Bu yaklaşım, hassas verilere doğrudan erişimi engeller ve geliştiricileri anahtarı almak için daha kontrollü bir yöntem (örneğin, kimlik doğrulamayı işleyen bir fonksiyon) kullanmaya zorlar.
apply Handler'ı ile Fonksiyon Çağrılarını Doğrulama
apply handler'ı, fonksiyon çağrılarını yakalamanıza ve fonksiyona iletilen argümanları doğrulamanıza olanak tanır. Bu, özellikle fonksiyonların doğru türleri ve sayıda argüman aldığından emin olmak için kullanışlıdır.
Örnek: Argüman Türü Doğrulaması
Bir dikdörtgenin alanını hesaplayan bir fonksiyona iletilen argümanları doğrulayan bir Proxy oluşturalım.
function calculateArea(width, height) {
return width * height;
}
const validator = {
apply: function(target, thisArg, argumentsList) {
if (argumentsList.length !== 2) {
throw new Error('calculateArea requires exactly two arguments: width and height.');
}
const width = argumentsList[0];
const height = argumentsList[1];
if (typeof width !== 'number' || typeof height !== 'number') {
throw new TypeError('Width and height must be numbers.');
}
if (width <= 0 || height <= 0) {
throw new RangeError('Width and height must be positive values.');
}
return target.apply(thisArg, argumentsList);
}
};
const proxy = new Proxy(calculateArea, validator);
console.log(proxy(5, 10)); // Output: 50
try {
console.log(proxy(5)); // Throws Error
} catch (e) {
console.error(e);
}
try {
console.log(proxy('5', 10)); // Throws TypeError
} catch (e) {
console.error(e);
}
Bu örnekte, apply handler'ı, calculateArea fonksiyonuna iletilen argümanların sayısını ve türlerini kontrol eder. Argümanlar geçersizse, fonksiyon gerçekten yürütülmeden önce bir hata atar. Kritik satır `return target.apply(thisArg, argumentsList);` aslında orijinal fonksiyonu sağlanan argümanlarla yürütür.
construct Handler'ı ile Nesne Oluşturmayı Doğrulama
construct handler'ı, new operatörünü yakalamanıza ve kurucu fonksiyonuna iletilen argümanları doğrulamanıza olanak tanır. Bu, özellikle kurucular kullanılarak oluşturulan nesneler üzerindeki kısıtlamaları uygulamak için kullanışlıdır.
Örnek: Gerekli Özellikler
Bir User nesnesinin her zaman bir username ve email ile oluşturulduğundan emin olan bir Proxy oluşturalım.
class User {
constructor(username, email) {
this.username = username;
this.email = email;
}
}
const validator = {
construct: function(target, argumentsList) {
if (argumentsList.length !== 2) {
throw new Error('User constructor requires two arguments: username and email.');
}
const username = argumentsList[0];
const email = argumentsList[1];
if (typeof username !== 'string' || username.length === 0) {
throw new TypeError('Username must be a non-empty string.');
}
if (typeof email !== 'string' || !email.includes('@')) {
throw new TypeError('Email must be a valid email address.');
}
return new target(...argumentsList);
}
};
const UserProxy = new Proxy(User, validator);
const user1 = new UserProxy('john.doe', 'john.doe@example.com'); // Works fine
try {
const user2 = new UserProxy('john.doe'); // Throws Error
} catch (e) {
console.error(e);
}
try {
const user3 = new UserProxy('john.doe', 'invalid_email'); // Throws TypeError
} catch (e) {
console.error(e);
}
console.log(user1);
Bu örnekte, construct handler'ı, User kurucusuna iletilen argümanların sayısını ve türlerini kontrol eder. Argümanlar geçersizse, nesne oluşturulmadan önce bir hata atar. `return new target(...argumentsList);` satırı, sağlanan argümanları kullanarak sınıfın yeni bir örneğini oluşturur.
Gelişmiş Doğrulama Teknikleri
Temel tür kontrolü ve aralık doğrulamasının ötesinde, Proxy'ler daha gelişmiş doğrulama senaryoları için kullanılabilir.
Çapraz Özellik Doğrulaması
Farklı özellikler arasındaki ilişkileri doğrulamak için Proxy'leri kullanabilirsiniz. Örneğin, bir başlangıç tarihinin her zaman bir bitiş tarihinden önce olduğundan emin olmak isteyebilirsiniz.
const event = {
startDate: '2024-01-15',
endDate: '2024-01-20'
};
const validator = {
set: function(target, property, value) {
target[property] = value; // Set the value first
if (property === 'endDate' && target.startDate > target.endDate) {
throw new Error('End date must be after start date.');
}
return true;
}
};
const proxy = new Proxy(event, validator);
proxy.endDate = '2024-01-25'; // Works fine
try {
proxy.endDate = '2024-01-10'; // Throws Error
} catch (e) {
console.error(e);
}
Asenkron Doğrulama
Daha az yaygın olsa da, daha karmaşık doğrulama senaryoları için Proxy'leri asenkron işlemlerle kullanabilirsiniz. Bu, verileri harici kaynaklara karşı doğrulamak için API çağrıları yapmayı içerebilir.
Önemli Not: Proxy handler'ları içindeki asenkron işlemler karmaşık olabilir ve olay döngüsünü engellememek için dikkatli bir şekilde ele alınmalıdır. Genellikle asenkron doğrulamayı Proxy handler'ının dışında gerçekleştirmek ve ardından sonuçları uygulamak için Proxy'yi kullanmak daha iyidir.
Proxy'leri Doğrulama için Kullanmanın Faydaları
- Merkezi Doğrulama Mantığı: Proxy'ler, doğrulama mantığını tek bir yerde merkezileştirmenize olanak tanır, bu da bakımı ve güncellemeyi kolaylaştırır.
- Gelişmiş Kod Okunabilirliği: Doğrulama mantığını temel nesne mantığından ayırarak, kodunuzun okunabilirliğini ve sürdürülebilirliğini artırabilirsiniz.
- Gelişmiş Tür Güvenliği: Proxy'ler, tür güvenliğini uygulamaya yardımcı olur ve yanlış veri türlerinin neden olduğu hataların riskini azaltır.
- Esneklik ve Özelleştirme: Proxy'ler yüksek derecede esneklik sağlayarak, doğrulama kurallarını uygulamanızın özel ihtiyaçlarını karşılayacak şekilde özelleştirmenize olanak tanır.
Proxy'leri Kullanmanın Sınırlamaları
- Performans Yükü: Proxy'ler, nesne işlemlerinin yakalanması nedeniyle küçük bir performans yükü getirir. Bu yük, çoğu uygulama için genellikle ihmal edilebilir düzeydedir, ancak performansı kritik senaryolarda dikkate almak önemlidir.
- Uyumluluk: Proxy'ler modern tarayıcılarda ve Node.js'de desteklenirken, eski ortamlarda desteklenmezler. Eski tarayıcılarla uyumluluğu sağlamak için polyfill'ler kullanmanız gerekebilir.
- Hata Ayıklama: Proxy'ler kullanan kodda hata ayıklamak, nesne işlemlerinin yakalanması nedeniyle biraz daha zor olabilir. Ancak, modern geliştirme araçları Proxy'lerde hata ayıklama için iyi destek sağlar.
Proxy Handler Doğrulaması için En İyi Uygulamalar
- Handler'ları Basit Tutun: Performans yükünü en aza indirmek ve okunabilirliği artırmak için Proxy handler'ları içinde karmaşık mantıktan kaçının.
- Açık Hata Mesajları Sağlayın: Geliştiricilerin doğrulamanın neden başarısız olduğunu anlamalarına yardımcı olan bilgilendirici hata mesajları verin.
- Performansı Dikkate Alın: Özellikle performansı kritik uygulamalarda Proxy'lerin performans üzerindeki etkisine dikkat edin.
- Dikkatli Kullanın: Proxy'leri aşırı kullanmayın. Doğrulama ve diğer metaprogramlama görevleri için, net bir fayda sağladıkları yerlerde stratejik olarak kullanın.
- Kapsamlı Bir Şekilde Test Edin: Proxy tabanlı doğrulama mantığınızı, tüm senaryolarda beklendiği gibi çalıştığından emin olmak için kapsamlı bir şekilde test edin.
Doğrulama için Küresel Hususlar
Küresel bir kitle için uygulamalar geliştirirken, doğrulama kurallarını uygularken kültürel farklılıkları ve bölgesel farklılıkları dikkate almak önemlidir. İşte bazı temel hususlar:
- Tarih ve Saat Biçimleri: Farklı yerel ayarlar için tarih ve saat biçimlerini doğru bir şekilde işlemek için Moment.js veya date-fns gibi bir kitaplık kullanın. Örneğin, Amerika Birleşik Devletleri'nde tarihler genellikle AA/GG/YYYY olarak biçimlendirilirken, Avrupa'da tipik olarak GG/AA/YYYY olarak biçimlendirilir.
- Sayı Biçimleri: Ondalık ayırıcılar ve bin ayırıcılar dahil olmak üzere farklı sayı biçimlerinin farkında olun. Bazı ülkelerde ondalık ayırıcı olarak virgül kullanılırken, bazılarında nokta kullanılır.
- Para Birimi Biçimleri: Para birimi değerlerini, uygun para birimi sembolü ve ondalık duyarlılık dahil olmak üzere, kullanıcının yerel ayarı için doğru biçimde görüntüleyin.
- Adres Biçimleri: Adres biçimleri dünya çapında önemli ölçüde değişir. Uluslararası adres doğrulamasını ve biçimlendirmesini destekleyen bir kitaplık veya API kullanmayı düşünün.
- Telefon Numarası Biçimleri: Telefon numaralarının doğru girildiğinden emin olmak için uluslararası telefon numarası doğrulamasını ve biçimlendirmesini destekleyen bir kitaplık kullanın.
- Ad Biçimleri: Ad biçimlerinin kültürler arasında değişebileceğini unutmayın. Bazı kültürler, bir aile adından sonra verilen bir ad kullanırken, diğerleri bir aile adından sonra verilen bir ad kullanır. Ayrıca, bazı kültürlerde birden fazla ad veya soyadı bulunur.
- Karakter Setleri: Uygulamanızın farklı dillerde adları, adresleri ve diğer metin verilerini barındırmak için farklı karakter setlerini ve kodlamalarını desteklediğinden emin olun.
- Kültürel Hassasiyetler: Doğrulama kurallarını tasarlarken kültürel hassasiyetlere dikkat edin. Örneğin, bazı veri türleri bazı kültürlerde özel veya hassas olarak kabul edilebilir.
Örnek: Uluslararası Telefon Numarası Doğrulaması
// Assuming you're using a library like "google-libphonenumber"
import { parsePhoneNumberFromString, AsYouType } from 'google-libphonenumber';
function validatePhoneNumber(phoneNumber, countryCode) {
try {
const number = parsePhoneNumberFromString(phoneNumber, countryCode);
if (number && number.isValid()) {
return true;
} else {
return false;
}
} catch (error) {
return false; // Invalid phone number format
}
}
// Example Usage (Germany)
const isValidGermanNumber = validatePhoneNumber('+4917612345678', 'DE');
console.log('Is valid German number:', isValidGermanNumber); // Output: true
// Example Usage (United States)
const isValidUSNumber = validatePhoneNumber('+15551234567', 'US');
console.log('Is valid US number:', isValidUSNumber); // Output: true
Sonuç
JavaScript Proxy'leri, uygulamalarınızda doğrulama mantığını uygulamak için güçlü ve esnek bir mekanizma sağlar. Proxy handler'larından yararlanarak, nesne özelliklerine, fonksiyon argümanlarına ve nesne yapımına kısıtlamalar ve tür güvenliği uygulayabilir, bu da daha sağlam, sürdürülebilir ve güvenli bir koda yol açar. Proxy'leri kullanırken performans etkilerini ve uyumluluk sorunlarını dikkate almayı ve doğrulama mantığınızı her zaman kapsamlı bir şekilde test etmeyi unutmayın. Bu blog yazısında özetlenen en iyi uygulamaları izleyerek, JavaScript uygulamalarınızın kalitesini ve güvenilirliğini etkili bir şekilde artırabilir ve yerelleştirilmiş doğrulama stratejileriyle küresel bir kitleye hitap edebilirsiniz.